package org.cmcc.pcap.packet.tcp.https.xdr; /**
 *
 */
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.cmcc.awareness.AppInfo;
import com.cmcc.awareness.HttpProto;
import com.cmcc.xdr.entity.HttpEntity;
import com.cmcc.xdr.pcapparse.tcp.http.HttpProtoDistinguishStatistics;
import org.cmcc.pcap.packet.tcp.TcpPacketDecorator;
import org.cmcc.pcap.packet.tcp.http.HttpTransaction;
import org.cmcc.pcap.packet.util.DateUtil;


/**
 * @Date 2018年3月21日 下午6:48:11
 * @author Amish
 *
 */
public class HttpsXdrGenerate {
    private static Logger logger = LogManager.getLogger(HttpsXdrGenerate.class);

    public  ArrayList<HttpEntity> httpsEntityList = new ArrayList<HttpEntity>();

    public void generate(HttpTransaction httpsTransaction) {
        //TcpConnection tcpConnection = httpTransaction.tcpConnection;
        HttpEntity httpsEntity = new HttpEntity();

        StringBuilder server_name = null;
        Iterator<TcpPacketDecorator> i = httpsTransaction.al_splited.iterator();
        while(i.hasNext()){
            TcpPacketDecorator tpd = (TcpPacketDecorator) i.next();
            if(tpd.tcpPacket.getPayload() != null && tpd.tcpPacket.getPayload().getRawData().length > 0){
                //获取 server name
                StringBuilder server_name_temp = findHttpsClientHelloInfo(tpd);
                if(server_name_temp != null){
                    server_name = server_name_temp;
                    break;
                }
            }
        }
        String server_name_final = ""+server_name;
        logger.debug("server name is: "+server_name_final);


        String id_at_commonName = null;
        Iterator<TcpPacketDecorator> j = httpsTransaction.al_splited.iterator();
        while(j.hasNext()){
            TcpPacketDecorator tpd = (TcpPacketDecorator) j.next();
            if(tpd.tcpPacket.getPayload() != null && tpd.tcpPacket.getPayload().getRawData().length > 0){
                //获取 server name
                String id_at_commonName_temp = findHttpsCertificateInfo(tpd);
                if(id_at_commonName_temp != null && id_at_commonName_temp.length()>0){
                    id_at_commonName = id_at_commonName_temp;
                    break;
                }
            }
        }
        String id_at_commonName_final = id_at_commonName;
        logger.debug("id at commonName is: "+id_at_commonName_final);


        HttpProto httpproto = HttpProto.getInstance();
        AppInfo appInfo = httpproto.appDistinguish(server_name_final, id_at_commonName_final);
        if(appInfo!=null){
            HttpProtoDistinguishStatistics.getInstance().incrementDistinguish(appInfo);
            logger.debug("\n[ match app rule ]"+appInfo.toString());

            //填写APP_type
            String appType = appInfo.getNum_type();
            httpsEntity.setAppType(appType);

            //填写APP_SUB_type
            String appSubType = appInfo.getNum_subtype();
            httpsEntity.setAppSubType(appSubType);

        }else{
            HttpProtoDistinguishStatistics.getInstance().incrementUnDistinguish();
            logger.debug("\n[ do not match any app rule ]");
        }


        try {
            saveHttpsTcpConnectionToHttpsEntityList(httpsTransaction,httpsEntity);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            logger.error(e.getCause(), e);
        }

        //将每个httpEntity加入httpEntityList中，调用SaveHttpTransactionInfo的save方法保存到数据库中
        httpsEntityList.add(httpsEntity);

        /**
         * Interface
         * ProcedureStartTime
         * ProcedureEndTime
         * ProtocolType
         * AppType
         * AppSubType
         * IPAddressType
         * UserIPv4
         * UserIPv6
         * ......
         *
         * UL Data上行流量
         * DL Data下行流量
         * UL IP Packet上行IP包数
         * DL IP Packet下行IP包数
         * UL Disorder IP Packet上行TCP乱序报文数
         * DL Disorder IP Packet下行TCP乱序报文数
         * UL Retrans IP Packet上行TCP重传报文数
         * DL Retrans IP Packet下行TCP重传报文数
         * UL_IP_FRAG_PACKETS上行IP包的分片数
         * DL_IP_FRAG_PACKETS下行IP包的分片数
         * TCP SYN Num：TCP SYN次数
         * TCP SYN ACK Num：TCP SYN ACK的次数
         * TCP ACK Num：回复SYN ACK的ACK次数
         *
         *
         */

    }

    public void saveHttpsTcpConnectionToHttpsEntityList(HttpTransaction httpsTransaction,HttpEntity httpsEntity) throws ParseException{

        //填写IE和PORTAL,默认值是0，必须填写
        httpsEntity.setIe(0);
        httpsEntity.setPortal(0);

        Integer length = httpsTransaction.al_splited.size();
        logger.debug("tcpConnection length:"+length);

        TcpPacketDecorator tpdStart = httpsTransaction.al_splited.get(0);
        TcpPacketDecorator tpdEnd = httpsTransaction.al_splited.get(length-1);

        //计算procedure_start_time
        long timestamp_procedure_start_time = tpdStart.timestamp.getTime();
        Date procedureStartTime = DateUtil.formatTimestamp(timestamp_procedure_start_time);
        httpsEntity.setProcedureStartTime(procedureStartTime);

        //计算procedure_end_time
        long timestamp_procedure_end_time = tpdEnd.timestamp.getTime();
        Date procedureEndTime = DateUtil.formatTimestamp(timestamp_procedure_end_time);
        httpsEntity.setProcedureEndTime(procedureEndTime);

        //填写interface
        Integer interFace = 11;
        httpsEntity.setInterFace(interFace);

        //填写ip_address_type,ipv4是0,ipv6是1
        httpsEntity.setIpAddressType(0);

        //填写终端用户的ipv4地址
        String userIpv4 = tpdStart.ipPacket.getHeader().getSrcAddr().getHostAddress();
        userIpv4 = userIpv4.replace("/", "");
        httpsEntity.setUserIpv4(userIpv4);

        //填写终端用户ipv6地址
        String userIpv6 = "1111111111111111";
        httpsEntity.setUserIpv6(userIpv6);

        //填写终端用户port
        Integer userPort = tpdStart.tcpPacket.getHeader().getSrcPort().valueAsInt();
        httpsEntity.setUserPort(userPort);

        //填写L4 Protocol,0为tcp,1为udp
        httpsEntity.setL4Protocal(0);

        //填写服务端ipv4地址
        String appServerIpIpv4 = tpdStart.tcpPacket.getHeader().getDstPort().valueAsString();
        appServerIpIpv4 = appServerIpIpv4.replace("/", "");
        httpsEntity.setAppServerIpIpv4(appServerIpIpv4);

        //填写服务端ipv6地址
        String appServerIpIpv6 = "1111111111111111";
        httpsEntity.setAppServerIpIpv6(appServerIpIpv6);

        //填写服务端port
        Integer appServerPort = tpdStart.tcpPacket.getHeader().getDstPort().valueAsInt();
        httpsEntity.setAppServerPort(appServerPort);

        //填写上行流量DL DATA
        Integer ulData = httpsTransaction.UL_Data;
        httpsEntity.setUlData(ulData);

        //填写下行流量UL DATA
        Integer dlData = httpsTransaction.DL_Data;
        httpsEntity.setDlData(dlData);

        //填写上行ip包个数
        Integer ulIpPacket = httpsTransaction.UL_IP_Packet;
        httpsEntity.setUlIpPacket(ulIpPacket);

        //填写下行ip包葛素
        Integer dlIpPacket = httpsTransaction.DL_IP_Packet;
        httpsEntity.setDlIpPacket(dlIpPacket);

        //填写上行tcp乱序包
        Integer ulDisorderIpPacket = httpsTransaction.UL_Disorder_IP_Packet;
        httpsEntity.setUlDisorderIpPacket(ulDisorderIpPacket);

        //填写下行tcp乱序包
        Integer dlDisorderIpPacket = httpsTransaction.DL_Disorder_IP_Packet;
        httpsEntity.setDlDisorderIpPacket(dlDisorderIpPacket);

        //填写上行tcp重传包
        Integer ulRetransIpPacket = httpsTransaction.UL_Retrans_IP_Packet;
        httpsEntity.setUlRetransIpPacket(ulRetransIpPacket);

        //填写下行tcp重传包
        Integer dlRetransIpPacket = httpsTransaction.DL_Retrans_IP_Packet;
        httpsEntity.setDlRetransIpPacket(dlRetransIpPacket);

        //填写上行ip分片数
        Integer ulIpFragPackets = httpsTransaction.UL_IP_FRAG_PACKETS;
        httpsEntity.setUlIpFragPackets(ulIpFragPackets);

        //填写下行ip分片数
        Integer dlIpFragPackets = httpsTransaction.DL_IP_FRAG_PACKETS;
        httpsEntity.setDlIpFragPackets(dlIpFragPackets);

        //填写tcp中syn的次数
        Integer tcpSynNum = httpsTransaction.TCP_SYN_Num;
        httpsEntity.setTcpSynNum(tcpSynNum);

        //填写tcp中syn ack次数
        Integer tcpSynAckNum = httpsTransaction.TCP_SYN_ACK_Num;
        httpsEntity.setTcpSynAckNum(tcpSynAckNum);

        //填写回复syn ack的ack次数
        Integer tcpAckNum = httpsTransaction.TCP_ACK_Num;
        httpsEntity.setTcpAckNum(tcpAckNum);

        //填写protocol type，https是2
        httpsEntity.setProtocolType("2");


//		//获取 server name
//		ArrayList<TcpPacketDecorator> tcpPacketlist = httpsTransaction.al_splited;
//		for(int j=0; j< tcpPacketlist.size(); ++j){
//			TcpPacketDecorator temptpd = tcpPacketlist.get(j);
//			StringBuilder server_name_temp = findHttpsInfo(temptpd);
//			if(server_name_temp != null){
//				server_name = server_name_temp;
//				break;
//			}
//		}
//		logger.debug("server name is: "+server_name);

    }

    public String findHttpsCertificateInfo(TcpPacketDecorator tcpPacketDecorator){
//
//		byte[] tslhandshake1 = {(byte) 0x16};//记录层握手协议类型标识
//		byte[] tslhandshake2 = {(byte) 0x0b};//上层协议中的握手协议标识,certificate为Ox0b

        if(tcpPacketDecorator.tcpPacket.getRawData().length == 0){//该判断多余0613add
            return null;
        }

//		byte[] SSLhandshake1 = new byte[1];
//		byte[] SSLhandshake2 = new byte[1];
        byte[] data = tcpPacketDecorator.getData();//TODO https 由于不知道id-at-commonName字段具体会出现在第几个分段，暂时采用获取所有分段。性能问题?
        for (int i = 0; i < data.length-3; i++) {
            if (data[i] == 85 && data[i+1] == 4 && data[i+2] == 3){
                byte[] bytes = new byte[data[i + 4]];
                System.arraycopy(data,i+5, bytes,0, data[i + 4]);
                String idAtCommentName = new String(bytes, Charset.forName("utf-8"));
                boolean matches = Pattern.matches(".*\\.?.*\\..*", idAtCommentName);//TODO 正则匹配?穷举域名后缀?
                if (matches){
                    return idAtCommentName;
                }
            }
        }

        return null;

		/*System.arraycopy(data, 0, SSLhandshake1, 0, 1);//记录层握手协议类型为数据部分第1个字节
		System.arraycopy(data, 5, SSLhandshake2, 0, 1);//上层协议的握手协议标识为数据部分的第6个字节

		if(Arrays.equals(SSLhandshake1, tslhandshake1) && Arrays.equals(SSLhandshake2, tslhandshake2)){
			logger.info("-------------SSL-----Certificate");
			String dataString = printHexBinary(data);
			dataString.getBytes();
			List<String> list = new ArrayList<>();
			List<String> list1 = new ArrayList<>();
			String str[] = dataString.split(",");
			list = Arrays.asList(str);

		}*/
    }

    public StringBuilder findHttpsClientHelloInfo(TcpPacketDecorator tcpPacketDecorator){

        byte[] tslhandshake1 = {(byte) 0x16};//记录层握手协议类型标识
        byte[] tslhandshake2 = {(byte) 0x01};//上层协议中的握手协议标识，clienthello为Ox01

        if(tcpPacketDecorator.tcpPacket.getRawData().length == 0){//该判断多余0613add
            return null;
        }

        /**
         * ssl协议识别逻辑：
         * 由于TCP分段的原因，有些基于TCP连接的SSL没有握手阶段，且握手阶段可能存在client发往server的第二条信息由于乱序的原因比第一条client hello更早到达server端，
         * 而该条信息并不包括server_name字段，所以判断条件取以下三个条件的逻辑“与”：
         * 		1，端口号为“443”
         * 		2，判断记录层握手协议类型，即字段content type，该字段为0x16表示为握手协议，为0x17表示为应用数据，也即分段数据
         * 		3，判断上层协议中的握手协议标识，即字段handshake type，该字段为0x01表示为client hello，为0x02表示为server hello，
         * 		      相应的有certificate等握手阶段的对应字段值
         * 0727
         */

        byte[] SSLhandshake1 = new byte[1];
        byte[] SSLhandshake2 = new byte[1];
        byte[] data = tcpPacketDecorator.tcpPacket.getPayload().getRawData();
        System.arraycopy(data, 0, SSLhandshake1, 0, 1);//记录层握手协议类型为数据部分第1个字节
        System.arraycopy(data, 5, SSLhandshake2, 0, 1);//上层协议的握手协议标识为数据部分的第6个字节

        if(Arrays.equals(SSLhandshake1, tslhandshake1) && Arrays.equals(SSLhandshake2, tslhandshake2)){
            logger.info("-------------SSL-----Client Hello");
            String dataString = printHexBinary(data);//
            logger.info("-------------data + "+dataString);
            dataString.getBytes();
            List<String> list = new ArrayList<>();
            List<String> list1 = new ArrayList<>();
            String str[] = dataString.split(",");
            list = Arrays.asList(str);
            logger.info(list.size());

            /**
             * server_name字段获取
             * 通过每个字段含义以及长度顺次寻找server_name字段位置
             *
             */
            for (int i = 0; i < list.size(); i++) {

                //第44位为session ID length字段，该字段为0时，其下一个字段为Cipher Suites Length
                if(i == 43 && "00".equalsIgnoreCase(list.get(i))){


                    i = i + 1;

                    String tmp = list.get(i)+list.get(i+1);
                    int lengthCipher = Integer.parseInt(tmp,16);
                    i = i + 2 + lengthCipher;//该字段占2字节，故加2

                    int lengthCompression = Integer.parseInt((list.get(i)),16);
                    i = i + 1 + lengthCompression;//该字段占1字节，故加1

                    //数字9表示字段Compression Methods后到字段server_name length之前的长度，为固定的9字节
                    tmp = list.get(i + 9)+list.get(i + 9 + 1);
                    int lengthServerName = Integer.parseInt(tmp,16);
                    list1.addAll(list.subList(i + 9 + 2, i + 9 + 2 + lengthServerName));//该字段占2字节，故加2
                    break;
                }
                //第44位为session ID length字段，该字段不为0时，其下一个字段为session ID
                if(i == 43 && !"00".equalsIgnoreCase(list.get(i))){

                    int lengthSession = Integer.parseInt(list.get(i),16);
                    i = i + 1 + lengthSession;//该字段占1字节，故加1

                    int lengthCipher = Integer.parseInt((list.get(i)+list.get(i+1)),16);
                    i = i + 2 + lengthCipher;//该字段占2字节，故加2

                    int lengthCompression = Integer.parseInt((list.get(i)),16);
                    i = i + 1 + lengthCompression;//该字段占1字节，故加1

                    //数字9表示字段Compression Methods后到字段server_name length之前的长度，为固定的9字节
                    int lengthServerName = Integer.parseInt((list.get(i + 9)+list.get(i + 9 + 1)),16);
                    list1.addAll(list.subList(i + 9 + 2, i + 9 + 2 + lengthServerName));//该字段占2字节，故加2
                    break;

                }
            }

            logger.info(list1);

            //将该字段解析
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < list1.size(); i++){
                int decimal = Integer.parseInt(list1.get(i), 16);
                sb.append((char) decimal);
            }
            sb.toString();
            logger.info(sb);

            //logger.info("-------------ascii + "+convertHexToString(printHexBinary(data)));
            return sb;
        }

        return null;

    }

    public static String printHexBinary(byte[] data) {
        char[] hexCode = "0123456789ABCDEF".toCharArray();
        StringBuilder r = new StringBuilder(data.length * 2);
        for (byte b : data) {
            r.append(hexCode[(b >> 4) & 0xF]);
            r.append(hexCode[(b & 0xF)]);
            r.append(",");
        }
        return r.toString();
    }

}
